package com.ztspeech;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder.AudioSource;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;

import org.dragonnova.meetingclient.utils.LogUtil;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * 语音录制与播放
 * 格式：pcm wav
 * Author: xujie
 * Modify: songxy on 2015/10/21 10:29.
 */
public class AppAudio {

    private static final String TAG = "AppAudio";

    public static final int RECORDING = 20;
    public static final int RECORDED = 21;
    public static final int STOP_RECORD = 22;
    public static final int RECORD_ERROR = 23;

    public static final int PLAYING = 24;
    public static final int PLAYED = 25;
    public static final int PLAY_ERROR = 26;

    public static final int RECOGNIZING = 27;
    public static final int RECOGNIZED = 28;
    public static final int RECOGNIZED_FINISH = 37;
    public static final int RECOGNIZE_ERROR = 29;
    public static final int RECOGNIZE_TEMP_RESULT = 36;

    public static final int TRANSLATING = 30;
    public static final int TRANSLATED = 31;
    public static final int TRANSLATE_ERROR = 32;

    public static final int SYNTHESIZING = 33;
    public static final int SYNTHESIZED = 34;
    public static final int SYNTHESIZE_ERROR = 35;

    protected Handler mHandler;
    /*
	 * 录音和播音相关参数,采样频率为16k表示1秒取16k个数据，pcm_16bit则表示一个数据用16bit，即2byte大小来储存，则一秒钟的音频
	 * 数据大小为16*2=32k。
	 */
    private static final int recAudioSource = AudioSource.MIC;
    private static final int playAudioSource = AudioManager.STREAM_MUSIC;
    private static final int sampleRateInHz = 16000;
    private static final int recChannelConfig = AudioFormat.CHANNEL_IN_MONO;
    private static final int playChannelConfig = AudioFormat.CHANNEL_OUT_MONO;
    private static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    private static int BuffSizeInBytes = 0;
    // 一帧音频时长为10ms
    static final int waveFrameMs = 10;
    // 一帧音频数据大小
    static final int waveFrameSize = sampleRateInHz * 2 * waveFrameMs / 1000;
    // 最大录音时间为60s
    static final int maxWaveSec = 60;
    //
    static final int MAX_WAVE_SIZE = sampleRateInHz * 2 * maxWaveSec;
    // 总的音频波形数据字节
    private byte[] allWaveData = new byte[MAX_WAVE_SIZE];
    // 已经记录的音频数据大小
    private int waveDataSize = 0;

    private AudioRecord audioRecord;
    private AudioTrack audioTrack;
    private Thread recThread;
    private Thread playThread;
    private boolean isRecording = false;
    private boolean isPlaying = false;

    private static final int RECORDER_BPP = 16;
    //默认录音文件的存储位置
    private static final String AUDIO_RECORDER_FOLDER = "org.dragonnova.wifihsserver/AudioRecorder";
    private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw";
    //转换的Wav格式文件的存储位置
    private File voiceFile;
    private byte buffer[] = new byte[16000];
    private File mAudioSavedFile;

    /**
     * 构造函数
     */
    public AppAudio(Handler handler) {
        this.mHandler = handler;
        // 缓冲区大小，约为1s数据大小的1/25，如32k/25=1280
        BuffSizeInBytes = AudioRecord.getMinBufferSize(
                sampleRateInHz, recChannelConfig, audioFormat);
        //创建保存录音文件的文件夹
        String filepath = Environment.getExternalStorageDirectory().getAbsolutePath();
        mAudioSavedFile = new File(filepath, AUDIO_RECORDER_FOLDER);
        if (!mAudioSavedFile.exists()) {
            mAudioSavedFile.mkdirs();
        }
    }

    //返回是否正在录音
    public boolean isRecording() {
        return this.isRecording;
    }

    //返回是否在播放
    public boolean isPlaying() {
        return this.isPlaying;
    }

    public void setAllWaveData(byte[] allWaveData) {
        this.allWaveData = new byte[sampleRateInHz * 2 * maxWaveSec];
        this.allWaveData = allWaveData;
    }

    public byte[] getAllWaveData() {
        return allWaveData;
    }

    public int getWaveFrameSize() {
        return waveFrameSize;
    }

    public void setWaveDataSize(int waveDataSize) {
        this.waveDataSize = waveDataSize;
    }

    public int getWaveDataSize() {
        return waveDataSize;
    }

    /**
     * 初始化播放
     */
    private void initAudioTrack() {
        if (audioTrack == null) {
            audioTrack = new AudioTrack(playAudioSource, sampleRateInHz,
                    playChannelConfig, audioFormat, BuffSizeInBytes,
                    AudioTrack.MODE_STREAM);
        }
    }

    /**
     * 初始化录音
     */
    private void initAudioRecord() {
        if (audioRecord == null) {
            audioRecord = new AudioRecord(recAudioSource, sampleRateInHz,
                    recChannelConfig, audioFormat, BuffSizeInBytes);
        }
    }

    /**
     * 开始录音，返回录音文件路径
     */
    public String startRecord() {
        initAudioRecord();
        voiceFile = getNewFileName();
        recThread = new Thread(new AudioRecordThread());
        audioRecord.startRecording();
        isRecording = true;
        recThread.start();
        return voiceFile.getAbsolutePath();
    }

    /**
     * 开始播放
     */
    public void playRecord(byte[] data) {
        initAudioTrack();
        if (checkAudioTrack()) {
            try {
                playThread.join();
            } catch (InterruptedException e) {
                LogUtil.e(TAG, "Play Thread Interrupted " + e.getMessage());
            }
            playThread = null;
        }
        if (data != null) {
            try {
                InputStream is = new ByteArrayInputStream(data);
                playThread = new Thread(new AudioTrackThread(is));
                isPlaying = true;
                playThread.start();
                audioTrack.play();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 2015.12.23 from hucn:重写playRecord方法。传入文件路径
     * 开始播放
     */
    public void playRecord(String vf) {
        initAudioTrack();
        File voiceFile = new File(vf);
        if (checkAudioTrack()) {
            try {
                playThread.join();
            } catch (InterruptedException e) {
                LogUtil.e(TAG, "Play Thread Interrupted " + e.getMessage());
            }
            playThread = null;
        }
        if (voiceFile.exists()) {
            try {
                InputStream is = new FileInputStream(voiceFile);
                is.skip(0x2c);
                playThread = new Thread(new AudioTrackThread(is));
                isPlaying = true;
                playThread.start();
                audioTrack.play();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 停止录音
     */
    public void stopRecord() {
        isRecording = false;
        if (recThread != null) {
            try {
                recThread.join();
                recThread = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            audioRecord.stop();
        }
        copyWaveFile(getTempFile(), voiceFile);
    }

    /**
     * 2015.12.31 from hucn：当强制停止时，并不需要保存文件
     * 停止录音，且不需要保存录音文件
     */
    public void stopRecordNotSave() {
        isRecording = false;
        if (recThread != null) {
            try {
                recThread.join();
                recThread = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            audioRecord.stop();
        }
    }

    /**
     * 停止播放
     */
    public void stopTrack() {
        isPlaying = false;
        if (audioTrack != null) {
            if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) audioTrack.stop();
        }
    }

    /**
     * 销毁AudioRecord
     */
    public void destroyAudioRecord() {
        isRecording = false;
        if (audioRecord != null) {
            if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING)
                audioRecord.stop();
            audioRecord.release();
            audioRecord = null;
        }
    }

    /**
     * 销毁AudioTrack
     */
    public void destroyAudioTrack() {
        isPlaying = false;
        if (audioTrack != null) {
            if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) audioTrack.stop();
            audioTrack.release();
            audioTrack = null;
        }
    }

    /**
     *检查是否正在播放
     */
    private boolean checkAudioTrack() {
        if (isPlaying && playThread.isAlive()) {
            mHandler.obtainMessage(PLAYING).sendToTarget();
            return true;
        }
        return false;
    }

    /**
     * 录音线程
     */
    private class AudioRecordThread implements Runnable {
        private static final String TAG = "AudioRecordThread";

        @Override
        public void run() {
            LogUtil.d(TAG, "AudioRecordThread run");
            byte[] waveData = new byte[BuffSizeInBytes];
            File filename = getTempFile();
            FileOutputStream fos = null;
            waveDataSize = 0;
            try {
                fos = new FileOutputStream(filename,false);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if (fos != null) {
                while (isRecording) {
                    // 返回的readSize为1280，即为录音缓冲区大小
                    int readSize = audioRecord.read(waveData, 0, BuffSizeInBytes);
                    // 录音有效(2016.1.20 修改为&&)
                    if (AudioRecord.ERROR_INVALID_OPERATION != readSize &&
                            AudioRecord.ERROR_BAD_VALUE != readSize) {
                        try {
                            fos.write(waveData);
                        } catch (IOException e) {
                            LogUtil.e(TAG, e.getMessage());
                        }
                        // 录音时间超过60s
                        if (waveDataSize + readSize > allWaveData.length) {
                            LogUtil.d(TAG, "max wave userdata size reached");
                            Message msg = mHandler.obtainMessage(STOP_RECORD);
                            msg.arg1 = -1;
                            mHandler.sendMessage(msg);
                            break;
                        }
                        // 将每次记录的音频数据放入总的音频数据中
                        System.arraycopy(waveData, 0, allWaveData, waveDataSize,
                                readSize);
                        waveDataSize += readSize;
                        Message msg = mHandler.obtainMessage(RECORDING);
                        msg.obj = allWaveData;
                        mHandler.sendMessage(msg);
                        LogUtil.d(TAG, "read userdata:" + readSize + " total size:"
                                + waveDataSize);
                        LogUtil.e(TAG, "recording........");
                    } else {
                        Message msg = mHandler.obtainMessage(RECORD_ERROR);
                        mHandler.sendMessage(msg);
                        break;
                    }
                }
            }
            try {
                if (fos != null) fos.close();
            } catch (IOException e) {
                LogUtil.e(TAG, e.getMessage());
            }
            Message msg = mHandler.obtainMessage(AppAudio.RECORDED);
            mHandler.sendMessage(msg);
            LogUtil.e(TAG, "recorded........");
        }
    }

    /**
     * 播放线程
     */
    private class AudioTrackThread implements Runnable {
        private static final String TAG = "AudioTrackThread";
        private byte[] data;
        private InputStream is;

        public AudioTrackThread(InputStream is) {
            this.is = is;
        }

        public AudioTrackThread(byte[] data) {
            this.data = data;
        }

        @Override
        public void run() {
            LogUtil.d(TAG, "AudioTrackThread run");
            try {
                if (data != null && data.length > 0) {
                    is = new ByteArrayInputStream(data);
                }
                int readedLength;
                while ((readedLength = is.read(buffer)) >= 0 && isPlaying) {
                    audioTrack.write(buffer, 0, readedLength);
                    LogUtil.e(TAG, "playing........");
                }
                Message msg = mHandler.obtainMessage(PLAYED);
                LogUtil.e(TAG, "played........");
                mHandler.sendMessage(msg);
            } catch (IOException e) {
                e.printStackTrace();
                Message msg = mHandler.obtainMessage(PLAY_ERROR);
                mHandler.sendMessage(msg);
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                stopTrack();
            }
        }
    }

    /**
     * 生成Wav文件的路径
     */
    private File getNewFileName() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
        Date curDate = new Date(System.currentTimeMillis());
        return new File(mAudioSavedFile.getAbsolutePath(), dateFormat.format(curDate) + ".wav");
    }

    /**
     * 获取原始录音文件的路径
     */
    private File getTempFile() {
        if(!mAudioSavedFile.exists())mAudioSavedFile.mkdirs();
        return new File(mAudioSavedFile, AUDIO_RECORDER_TEMP_FILE);
    }

    /**
     * 删除临时录音文件
     */
    private void deleteTempFile() {
        File file = getTempFile();
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 从一个文件复制到另外一个文件
     */
    private void copyWaveFile(File inFilename, File outFilename) {
        FileInputStream in = null;
        FileOutputStream out = null;
        long totalAudioLen = 0;
        long totalDataLen = totalAudioLen + 36;
        long longSampleRate = sampleRateInHz;
        int channels = 1;
        long byteRate = RECORDER_BPP * sampleRateInHz * channels / 8;

        byte[] data = new byte[BuffSizeInBytes];

        try {
            in = new FileInputStream(inFilename);
            out = new FileOutputStream(outFilename);
            totalAudioLen = in.getChannel().size();
            totalDataLen = totalAudioLen + 36;

            WriteWaveFileHeader(out, totalAudioLen, totalDataLen,
                    longSampleRate, channels, byteRate);

            while (in.read(data) != -1) {
                out.write(data);
            }

            in.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 录音文件的写法
     */
    private void WriteWaveFileHeader(
            FileOutputStream out, long totalAudioLen,
            long totalDataLen, long longSampleRate, int channels,
            long byteRate) throws IOException {

        byte[] header = new byte[44];

        header[0] = 'R';  // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f';  // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16;  // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1;  // format = 1
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (longSampleRate & 0xff);
        header[25] = (byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (byte) ((longSampleRate >> 24) & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        header[32] = (byte) (1 * 16 / 8);  // block align
        header[33] = 0;
        header[34] = RECORDER_BPP;  // bits per sample
        header[35] = 0;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
        out.write(header, 0, 44);
    }
}
